#include "voice_aiui_node.h"
#include "ros/ros.h"

REIListener* g_pListener = nullptr; // NOLINT

IAIUIAgent* g_pAgent = nullptr;

string gSyncSid;
string gVoiceCloneResId;
int gPcmPlayerIndex = -1;

REIListener::REIListener()
{
    // 创建内置的pcm播放器，并初始化，设置回调，启动起来
    aiui_pcm_player_create();

    int count = aiui_pcm_player_get_output_device_count();
    for (int i = 0; i < count; i++) {
        cout << "pcm player index: " << i
                << " device name: " << aiui_pcm_player_get_device_name(i) << endl;
    }
    cout << "user pcm player index: " << gPcmPlayerIndex << endl;

    aiui_pcm_player_init(gPcmPlayerIndex);
    // aiui_pcm_player_set_callbacks(
    //     onStarted, onPaused, onResumed, onStopped, onProgress, onError);
    aiui_pcm_player_start();

    std::shared_ptr<TtsHelperListener> listener = std::make_shared<TtsHelperListener>();
    m_pTtsHelper = std::make_shared<StreamNlpTtsHelper>(listener);
    m_pTtsHelper->setTextMinLimit(20);
}

void REIListener::processIntentJson(Json::Value& params,
                           Json::Value& intentJson,
                           std::string& resultStr,
                           int eosRsltTime,
                           std::string& sid)
{
    int rc = intentJson["rc"].asInt();

    Json::Value answerJson = intentJson["answer"];
    string answer = answerJson["text"].asString();

    // 正常nlp结果（AIUI通用语义模型返回的语义结果）
    cout << "----------------------------------" << endl;
    cout << "params: " << params.asString() << endl;
    cout << "nlp: " << resultStr << endl;
    cout << "eos_result=" << eosRsltTime << "ms" << endl;
    cout << "结果解析：" << endl;
    cout << "sid=" << sid << endl;
    cout << "text（请求文本）: " << intentJson["text"].asString() << endl;
    cout << "rc=" << rc << ", answer（应答语）: " << answer << endl;

//     // 极速交互不支持主动合成，只有非主动交互才支持
// #ifndef USE_RAPID_INTERACTION
//     if (!answer.empty()) {
//         startTTS(answer);
//     }
// #endif
}

void REIListener::handleEvent(const IAIUIEvent& event)
{
    switch (event.getEventType()) {
        // SDK状态
        case AIUIConstant::EVENT_STATE: {
            switch (event.getArg1()) {
                case AIUIConstant::STATE_IDLE: {
                    // 空闲状态，即最初始的状态
                    cout << "EVENT_STATE: STATE_IDLE" << endl;
                } break;

                case AIUIConstant::STATE_READY: {
                    // 准备好状态（待唤醒），可以进行唤醒
                    cout << "EVENT_STATE: STATE_READY" << endl;
                } break;

                case AIUIConstant::STATE_WORKING: {
                    // 工作状态（即已唤醒状态），可以语音交互，也可以再次唤醒
                    cout << "EVENT_STATE: STATE_WORKING" << endl;
                } break;
            }
        } break;
        // 唤醒事件
        case AIUIConstant::EVENT_WAKEUP: {
            cout << "EVENT_WAKEUP: " << event.getInfo() << endl;

            // 唤醒时停止播放
            aiui_pcm_player_stop();
        } break;

        // 休眠事件，即一段时间无有效交互或者外部主动要求，SDK会自动进入STATE_READY状态
        case AIUIConstant::EVENT_SLEEP: {
            // arg1用来区分休眠类型，是自动休眠还是外部要求，可参考AIUIConstant.h中EVENT_SLEEP的注释
            cout << "EVENT_SLEEP: arg1=" << event.getArg1() << endl;
        } break;

        // VAD事件，如语音活动检测
        case AIUIConstant::EVENT_VAD: {
            // arg1为活动类型
            switch (event.getArg1()) {
                case AIUIConstant::VAD_BOS_TIMEOUT: {
                    cout << "EVENT_VAD: VAD_BOS_TIMEOUT" << endl;
                } break;

                // 检测到前端点，即开始说话
                case AIUIConstant::VAD_BOS: {
                    cout << "EVENT_VAD: BOS" << endl;
                } break;

                // 检测到后端点，即说话结束
                case AIUIConstant::VAD_EOS: {
                    cout << "EVENT_VAD: EOS" << endl;
                } break;

                // 音量，arg2为音量级别（0-30）
                case AIUIConstant::VAD_VOL: {
                    //cout << "EVENT_VAD: vol=" << event.getArg2() << endl;
                } break;
            }
        } break;

        // 结果事件
        case AIUIConstant::EVENT_RESULT: {
            Json::Value bizParamJson;
            Json::Reader reader;

            if (!reader.parse(event.getInfo(), bizParamJson, false)) {
                cout << "parse error! info=" << event.getInfo() << endl;
                break;
            }

            Json::Value& data = (bizParamJson["data"])[0];
            Json::Value& params = data["params"];
            Json::Value& content = (data["content"])[0];

            string sub = params["sub"].asString();
            if (sub != "iat" && sub != "nlp" && sub != "tts" && sub != "cbm_tidy" && sub != "cbm_semantic") {
                return;
            }

            // sid即唯一标识一次会话的id
            string sid = event.getData()->getString("sid", "");
            if (sub == "iat") {
                if (sid != mCurIatSid) {
                    cout << "**********************************" << endl;
                    cout << "sid=" << sid << endl;
                    mCurIatSid = sid;
                    // 新的会话，清空之前识别缓存
                    mIatTextBuffer.clear();
                    mStreamNlpAnswerBuffer.clear();
                    m_pTtsHelper->clear();
                    mIntentCnt = 0;
                }
            } else if (sub == "tts") {
                if (sid != mCurTtsSid) {
                    cout << "**********************************" << endl;
                    cout << "sid=" << sid << endl;
                    mTtsLen = 0;
                    mCurTtsSid = sid;
                }
            }

            Json::Value empty;
            string cnt_id = content.get("cnt_id", empty).asString();

            int dataLen = 0;

            // 注意：当buffer里存字符串时也不是以0结尾，当使用C语言时，转成字符串则需要自已在末尾加0
            const char* buffer = event.getData()->getBinary(cnt_id.c_str(), &dataLen);

            if (sub == "tts") {
                // 语音合成结果，返回url或者pcm音频
                cout << "tts: " << content.toString() << endl;

#ifdef USE_POST_SEMANTIC_TTS //使用语义后合成
                //多意图的语义后合成的tts信息,可以知晓当前的tts音频流是哪个意图的。
                if (mIntentCnt > 1) {
                    int ttsOtherDataLen = 0;
                    const char* ttsOtherBuffer = event.getData()->getBinary("1", &ttsOtherDataLen);
                    string ttsOtherResultStr = string(ttsOtherBuffer, ttsOtherDataLen);
                    Json::Value ttsOtherResultJson;
                    if (reader.parse(ttsOtherResultStr, ttsOtherResultJson, false)) {
                        Json::Value metaTtsJson;
                        Json::Value textJson =
                            ttsOtherResultJson["cbm_meta"].get("text", metaTtsJson);
                        if (reader.parse(textJson.asString(), metaTtsJson, false)) {
                            int intentIndex = metaTtsJson["tts"]["intent"].asInt();
                            cout << "tts intent index: " << intentIndex
                                    << " , des: " << ttsOtherResultJson.asString().c_str() << endl;
                        }
                    }
                }
#endif

                Json::Value&& isUrl = content.get("url", empty);
                if (isUrl.asString() == "1") {
                    // 云端返回的是url链接，可以用播放器播放
                    cout << "tts_url=" << string(buffer, dataLen) << endl;
                } else {
                    // 云端返回的是pcm音频，分成一块块流式返回
                    int progress = 0;
                    int dts = content["dts"].asInt();

                    string tag = event.getData()->getString("tag", "");
                    if (tag.find("stream_nlp_tts") == 0) {
                        // 流式语义应答的合成
                        m_pTtsHelper->onOriginTtsData(tag, bizParamJson, buffer, dataLen);
                    } else {
                        // 只有碰到开始块和(特殊情况:合成字符比较少时只有一包tts，dts = 2)，开启播放器
                        if (dts == AIUIConstant::DTS_BLOCK_FIRST
                            || dts == AIUIConstant::DTS_ONE_BLOCK ||
                            (dts == AIUIConstant::DTS_BLOCK_LAST && 0 == mTtsLen)) {

                            if (aiui_pcm_player_get_state() != PCM_PLAYER_STATE_STARTED) {
                                int ret = aiui_pcm_player_start();
                                cout << "pcm start ret:" << ret << endl;
                            }
                        }

                        mTtsLen += dataLen;
                        aiui_pcm_player_write(0, buffer, dataLen, dts, progress);

                        // 若要保存合成音频，请打开以下开关
#if 0
                        // 音频开始
                        if (dts == AIUIConstant::DTS_BLOCK_FIRST ||
                            dts == AIUIConstant::DTS_ONE_BLOCK ||
                            /* 特殊情况:合成字符比较少时只有一包数据而且状态是２ */
                            (dts == AIUIConstant::DTS_BLOCK_LAST && 0 == mTtsLen)) {
                            mFs.open("tts.pcm", ios::binary | ios::out);
                        }

                        if (dataLen > 1) {
                            mFs.write(buffer, dataLen);
                        }

                        // 音频结束
                        if (dts == AIUIConstant::DTS_BLOCK_LAST || dts == AIUIConstant::DTS_ONE_BLOCK) {
                            mFs.close();
                        }
#endif
                    }
                }
            } else if (sub == "iat") {
                // 语音识别结果
                string resultStr = string(buffer, dataLen);     // 注意：这里不能用string resultStr = buffer，因为buffer不一定以0结尾
                Json::Value resultJson;
                if (reader.parse(resultStr, resultJson, false)) {
                    Json::Value textJson = resultJson["text"];
                    bool isWpgs = false;
                    if (textJson.isMember("pgs")) {
                        isWpgs = true;
                    }

                    if (isWpgs) {
                        mIatTextBuffer = IatResultUtil::parsePgsIatText(textJson);
                    } else {
                        // 结果拼接起来
                        mIatTextBuffer.append(IatResultUtil::parseIatResult(textJson));
                    }

                    // 是否是该次会话最后一个识别结果
                    bool isLast = textJson["ls"].asBool();
                    if (isLast) {
                        cout << "params: " << params.asString() << endl;
                        cout << "iat: " << mIatTextBuffer << endl;

                        mIatTextBuffer.clear();
                    }
                }
            } else if (sub == "nlp") {
                // 语义理解结果
                // 注意：这里不能用string resultStr = buffer，因为buffer不一定以0结尾
                string resultStr = string(buffer, dataLen);

                // 从说完话到语义结果返回的时长
                long eosRsltTime = event.getData()->getLong("eos_rslt", -1);

                Json::Value resultJson;
                if (reader.parse(resultStr, resultJson, false)) {
                    // 判断是否为有效结果
                    if (resultJson.isMember("intent") &&
                        resultJson["intent"].isMember("rc")) {
                        // AIUI v1的语义结果
                        Json::Value intentJson = resultJson["intent"];
                        processIntentJson(params, intentJson, resultStr, eosRsltTime, sid);
                    } else if (resultJson.isMember("nlp")) {
                        // AIUI v2的语义结果
                        Json::Value nlpJson = resultJson["nlp"];
                        string text = nlpJson["text"].asString();

                        if (text.find("{\"intent\":") == 0) {
                            // 通用语义结果
                            Json::Value textJson;
                            if (reader.parse(text, textJson, false)) {
                                Json::Value intentJson = textJson["intent"];
                                processIntentJson(params, intentJson, resultStr, eosRsltTime, sid);
                            }
                        } else {
                            // 大模型语义结果
                            // 流式nlp结果里面有seq和status字段
                            int seq = nlpJson["seq"].asInt();
                            int status = nlpJson["status"].asInt();

                            if (status == 0) {
                                mStreamTtsIndex = 0;
                            }

                            /* 多意图取最后一次问题的结果进行tts合成 */
                            if (mIntentCnt > 1) {
                                int currentIntentIndex = 0;
                                Json::Value metaNlpJson;
                                Json::Value textJson = resultJson["cbm_meta"].get("text", metaNlpJson);
                                if (reader.parse(textJson.asString(), metaNlpJson, false)) {
                                    currentIntentIndex = metaNlpJson["nlp"]["intent"].asInt();
                                    if ((mIntentCnt - 1) != currentIntentIndex) {
                                        cout << "ignore nlp:" << resultStr << endl;
                                        return;
                                    }
                                } else {
                                    cout << "ignore nlp:" << resultStr << endl;
                                    return;
                                }
                            }

#ifndef USE_POST_SEMANTIC_TTS
                            // 如果使用应用的语义后合成不需要在调用下面的函数否则tts的播报会重复
                            m_pTtsHelper->addText(text, mStreamTtsIndex++, status);
#endif

                            cout << "----------------------------------" << endl;
                            cout << "params: " << params.asString() << endl;
                            cout << "nlp: " << resultStr << endl;

                            if (seq == 0) {
                                long eosRsltTime = event.getData()->getLong("eos_rslt", -1);
                                cout << "eos_result=" << eosRsltTime << "ms" << endl;
                            }

                            cout << "结果解析：" << endl;
                            cout << "sid=" << sid << endl;
                            cout << "seq=" << seq << ", status=" << status << ", answer（应答语）: " << text << endl;
                            cout << "fullAnswer=" << (mStreamNlpAnswerBuffer.append(text)) << endl;

                            if (status == 2) {
                                mStreamNlpAnswerBuffer.clear();
                            }
                        }
                    } else {
                        // 无效结果，把原始结果打印出来
                        cout << "----------------------------------" << endl;
                        cout << "nlp: " << resultStr << endl;
                        cout << "sid=" << sid << endl;
                    }
                }
            } else if (sub == "cbm_tidy") {
                // 意图拆分的结果
                string intentStr = string(buffer, dataLen); // 注意：这里不能用string resultStr = buffer，因为buffer不一定以0结尾
                Json::Value tmpJson;
                if (reader.parse(intentStr, tmpJson, false)) {
                    Json::Value intentTextJson = tmpJson["cbm_tidy"]["text"];
                    if (!intentTextJson.empty() &&
                        reader.parse(intentTextJson.asString(), tmpJson, false)) {
                        mIntentCnt = tmpJson["intent"].size();
                        cout << "cbm_intent_cnt: " << mIntentCnt
                                << " text: " << tmpJson.toString() << endl;
                    }
                }
            } else {
                // 其他结果
                string resultStr = string(buffer, dataLen);     // 注意：这里不能用string resultStr = buffer，因为buffer不一定以0结尾

                cout << sub << ": " << event.getInfo() << endl << resultStr << endl;
            }
        } break;

        // 与CMD命令对应的返回结果，arg1为CMD类型，arg2为错误码
        case AIUIConstant::EVENT_CMD_RETURN: {
            if (AIUIConstant::CMD_BUILD_GRAMMAR == event.getArg1()) {
                // 语法构建命令的结果
                // 注：需要集成本地esr引擎才能构建语法
                if (event.getArg2() == 0) {
                    cout << "build grammar success." << endl;
                } else {
                    cout << "build grammar, error=" << event.getArg2() << ", des=" << event.getInfo() << endl;
                }
            } else if (AIUIConstant::CMD_UPDATE_LOCAL_LEXICON == event.getArg1()) {
                // 更新本地语法槽的结果
                if (event.getArg2() == 0) {
                    cout << "update lexicon success" << endl;
                } else {
                    cout << "update lexicon, error=" << event.getArg2() << "des=" << event.getInfo() << endl;
                }
            } else if (AIUIConstant::CMD_CLONE_VOICE == event.getArg1()) {
                //声音复刻
                int dtype = event.getData()->getInt("sync_dtype", -1);
                int retCode = event.getArg2();
                string dataTypeStr;
                if (dtype == AIUIConstant::VOICE_CLONE_REG) { //注册资源
                    dataTypeStr = "注册音频资源";
                } else if (dtype == AIUIConstant::VOICE_CLONE_DEL) { //删除资源
                    dataTypeStr = "删除资源";
                } else if (dtype == AIUIConstant::VOICE_CLONE_RES_QUERY) { //查询资源
                    dataTypeStr = "查询资源";
                }

                if (AIUIConstant::SUCCESS == retCode ) {
                    // 上传成功，会话的唯一id，用于反馈问题的日志索引字段，注意留存
                    // 注：上传成功立即生效
                    string sid = event.getData()->getString("sid", "");
                    // 获取上传调用时设置的自定义tag
                    string tag = event.getData()->getString("tag", "");
                    // 获取上传调用耗时，单位：ms
                    long timeSpent = event.getData()->getLong("time_spent", -1);
                    cout << "声音复刻" << dataTypeStr << "成功"
                            << "，耗时：" << timeSpent
                            << "ms, sid=" + sid + "，tag=" + tag;
                    if (dtype == AIUIConstant::VOICE_CLONE_REG) {
                        string resId = event.getData()->getString("res_id", "");
                        cout << "，res id = " << resId << endl;

                        //保存声音复刻的的res id
                        gVoiceCloneResId = resId;
                        fstream fs;
                        fs.open("./voice_clone_reg_id.txt", ios::binary | ios::out);
                        fs.write(resId.c_str(), resId.length());
                        fs.close();
                    } else if (dtype == AIUIConstant::VOICE_CLONE_RES_QUERY) {
                        string result = event.getData()->getString("result", "");
                        cout << ", result:" << endl;
                        cout << result << endl;
                    } else {
                        cout << endl;
                    }
                } else {
                    string result = event.getData()->getString("result", "");
                    cout << "声音复刻" << dataTypeStr << "失败，错误码：" <<
                        retCode << " info:" << event.getInfo() << " result:" << result << endl;
                }
            } else if (AIUIConstant::CMD_SYNC == event.getArg1()) {
                // 数据同步的返回
                int dtype = event.getData()->getInt("sync_dtype", -1);
                int retCode = event.getArg2();

            #if AIUI_VER == 2 || AIUI_VER == 3
                string dataTypeStr;
                string text;

                if (dtype == AIUIConstant::SYNC_DATA_UPLOAD) {
                    dataTypeStr = "上传实体";
                } else if (dtype == AIUIConstant::SYNC_DATA_DELETE) {
                    dataTypeStr = "删除实体";
                } else if (dtype == AIUIConstant::SYNC_DATA_DOWNLOAD) {
                    dataTypeStr = "下载实体";
                } else if (dtype == AIUIConstant::SYNC_DATA_SEE_SAY) {
                    dataTypeStr = "所见即可说";
                }

                if (AIUIConstant::SUCCESS == retCode ) {
                    // 上传成功，会话的唯一id，用于反馈问题的日志索引字段，注意留存
                    // 注：上传成功立即生效
                    gSyncSid = event.getData()->getString("sid", "");
                    // 获取上传调用时设置的自定义tag
                    string tag = event.getData()->getString("tag", "");
                    // 获取上传调用耗时，单位：ms
                    long timeSpent = event.getData()->getLong("time_spent", -1);
                    cout << "同步" << dataTypeStr << "成功"
                            << "，耗时：" << timeSpent
                            << "ms, sid=" + gSyncSid + "，tag=" + tag;
                    if (dtype == AIUIConstant::SYNC_DATA_UPLOAD) {
                        cout << "，你可以试着说“打电话给刘德华“" << endl;
                    } else {
                        cout << endl;
                    }
                    // 实体内容
                    if (dtype == AIUIConstant::SYNC_DATA_DOWNLOAD) {
                        text = event.getData()->getString("text", "");
                        cout << "下载的实体内容:\n" << Base64Util::decode(text) << endl;
                    }
                } else {
                    gSyncSid = "";
                    string result = event.getData()->getString("result", "");
                    cout << "同步" << dataTypeStr << "失败，错误码：" <<
                        retCode << " info:" << event.getInfo() << " result:" << result << endl;
                }
            #else
                if (dtype == AIUIConstant::SYNC_DATA_SCHEMA) {
                    if (AIUIConstant::SUCCESS == retCode) {
                        // 上传成功，记录上传会话的sid，以用于查询数据打包状态
                        // 注：上传成功并不表示数据打包成功，打包成功与否应以同步状态查询结果为准，数据只有打包成功后才能正常使用
                        gSyncSid = event.getData()->getString("sid", "");

                        // 获取上传调用时设置的自定义tag
                        string tag = event.getData()->getString("tag", "");

                        // 获取上传调用耗时，单位：ms
                        long timeSpent = event.getData()->getLong("time_spent", -1);

                        cout << "同步成功，"
                                << "耗时：" << timeSpent
                                << "ms, sid=" + gSyncSid + "，tag=" + tag +
                                    "，你可以试着说“打电话给刘德华“"
                                << endl;
                    } else {
                        gSyncSid = "";
                        cout << "同步失败，错误码：" << retCode << endl;
                    }
                }
            #endif
            }
        #if AIUI_VER == 1
            else if (AIUIConstant::CMD_QUERY_SYNC_STATUS == event.getArg1()) {
                // 数据同步状态查询的返回
                // 获取同步类型
                int syncType = event.getData()->getInt("sync_dtype", -1);
                if (AIUIConstant::SYNC_DATA_QUERY == syncType) {
                    // 若是同步数据查询，则获取查询结果，结果中error字段为0则表示上传数据打包成功，否则为错误码
                    string result = event.getData()->getString("result", "");

                    cout << "查询结果：" << result << endl;
                }
            }
        #endif
        } break;

        // 开始录音事件
        case AIUIConstant::EVENT_START_RECORD: {
            cout << "EVENT_START_RECORD " << endl;
        } break;

        // 停止录音事件
        case AIUIConstant::EVENT_STOP_RECORD: {
            cout << "EVENT_STOP_RECORD " << endl;
        } break;

        // 出错事件
        case AIUIConstant::EVENT_ERROR: {
            // 打印错误码和描述信息
            cout << "EVENT_ERROR: error=" << event.getArg1() << ", des=" << event.getInfo() << endl;
        } break;

        // 连接到服务器
        case AIUIConstant::EVENT_CONNECTED_TO_SERVER: {
            // 获取uid（为客户端在云端的唯一标识）并打印
            string uid = event.getData()->getString("uid", "");

            cout << "EVENT_CONNECTED_TO_SERVER, uid=" << uid << endl;
        } break;

        // 与服务器断开连接
        case AIUIConstant::EVENT_SERVER_DISCONNECTED: {
            cout << "EVENT_SERVER_DISCONNECTED " << endl;
        } break;

        //唤醒词操作的结果
        case AIUIConstant::EVENT_CAE_WAKEUP_WORD_RESULT: {
            int type = event.getArg1();
            int ret = event.getArg2();

            //保存唤醒词资源
            if (0 == ret && (type == AIUIConstant::CAE_GEN_WAKEUP_WORD ||
                                type == AIUIConstant::CAE_ADD_WAKEUP_WORD)) {
                int dataLen = 0;
                const char* buffer = event.getData()->getBinary("data", &dataLen);
                fstream fs;
                fs.open("./wakeup_word_res.bin", ios::binary | ios::out);
                fs.write(buffer, dataLen);
                fs.close();
            }

            if (ret != 0) {
                int dataLen = 0;
                const char* buffer = event.getData()->getBinary("error_msg", &dataLen);
                // 注意：这里不能用string errorMsg = buffer，因为buffer不一定以0结尾
                string errorMsg = string(buffer, dataLen);
                cout << "wakeup word operate fail, type = " << type << ", error code = " << ret
                        << ", error msg: " << errorMsg << endl;
            } else {
                cout << "wakeup word operate success, type = " << type << endl;
            }

        } break;

        //唤醒音频事件
        case AIUIConstant::EVENT_CAE_WAKEUP_AUDIO: {
            int type = event.getArg1();
            //保存唤醒音频
            if (type == AIUIConstant::CAE_WAKEUP_AUDIO) {;
                int dataLen = 0;
                const char* buffer = event.getData()->getBinary("audio", &dataLen);
                fstream fs;
                fs.open("wakeup_audio.pcm", ios::binary | ios::out);
                fs.write(buffer, dataLen);
                fs.close();
            }
        } break;
    };
}


void REIListener::onEvent(const IAIUIEvent& event)
{
    try {
        handleEvent(event);
    } catch (std::exception& e) {
        cout << e.what() << endl;
    }
}

string readFileAsString(const string& path)
{
    ifstream t(path, ios_base::in | ios::binary);
    if (!t.is_open()) {
        std::cout << "Error open file: " << path << " fail." << endl;
        return "";
    }
    string str((istreambuf_iterator<char>(t)), istreambuf_iterator<char>());

    return str;
}

void createAgent(bool more, const char* cfgPath)
{
    if (g_pAgent) {
        return;
    }
    string aiuiParams = readFileAsString(cfgPath);
    Json::Value paramJson;
    Json::Reader reader;
    if (reader.parse(aiuiParams, paramJson, false)) {
        if (more) {
            cout << paramJson.toString() << endl;
        }
        g_pListener->mMoreDetails = more;
        g_pAgent = IAIUIAgent::createAgent(paramJson.toString().c_str(), g_pListener);
        cout<<"创建aiui成功"<<endl;
    }
    if (!g_pAgent) {
        std::cout << string(cfgPath) << ", " << reader.getFormatedErrorMessages() << std::endl;
        cout<<"创建aiui失败"<<endl;
        return;
    }
}

/**
 * 销毁AIUIAgent对象。
 */
void destroyAgent()
{
    if (g_pAgent) {
        g_pAgent->destroy();
        g_pAgent = nullptr;
    }
}

/**
 * 唤醒AIUI。
 */
void wakeup()
{
    // 可以通过clear_data来控制是否要清除唤醒之前的数据（默认会清除），清除则唤醒之前的会话结果（tts除外）会被丢弃从而不再继续抛出
    SEND_AIUIMESSAGE4(AIUIConstant::CMD_WAKEUP, 0, 0, "clear_data=true");
}

/**
 * 重置唤醒，即回到待唤醒状态。
 */
void resetWakeup()
{
    SEND_AIUIMESSAGE1(AIUIConstant::CMD_RESET_WAKEUP);
}

/**
 * 开启AIUI服务，此接口是与stop()对应，调用stop()之后必须调用此接口才能继续与SDK交互。
 *
 * 注：AIUIAgent创建成功之后AIUI会自动开启，故若非调用过stop()则不需要调用start()。
 */
void start()
{
    SEND_AIUIMESSAGE1(AIUIConstant::CMD_START);
}

/**
 * 停止AIUI服务。
 */
void stop()
{
    SEND_AIUIMESSAGE1(AIUIConstant::CMD_STOP);
}

/**
 * 重置AIUI服务，相当于先调用stop()再调用start()。一般用不到。
 */
void resetAIUI()
{
    SEND_AIUIMESSAGE(AIUIConstant::CMD_RESET, 0, 0, "", nullptr);
}

void writeAudioFromLocal(bool repeat)
{
    if (!g_pAgent) {
        return;
    }

    ifstream testData(TEST_AUDIO_PATH, std::ios::in | std::ios::binary);

    if (testData.is_open()) {
        testData.seekg(0, std::ios::end);
        int total = testData.tellg();
        testData.seekg(0, std::ios::beg);

        char* audio = new char[total];
        testData.read(audio, total);
        testData.close();

        int offset = 0;
        int left = total;
        const int frameLen = 1280;
        char buff[frameLen];

        while (true) {
            if (left < frameLen) {
                if (repeat) {
                    offset = 0;
                    left = total;
                    continue;
                } else {
                    break;
                }
            }

            memset(buff, '\0', frameLen);
            memcpy(buff, audio + offset, frameLen);

            offset += frameLen;
            left -= frameLen;

            // frameData内存会在Message在内部处理完后自动release掉
            AIUIBuffer frameData = aiui_create_buffer_from_data(buff, frameLen);
            SEND_AIUIMESSAGE(AIUIConstant::CMD_WRITE, 0, 0, "data_type=audio,tag=audio-tag", frameData);

            // 必须暂停一会儿模拟人停顿，太快的话后端报错。1280字节16k采样16bit编码的pcm数据对应40ms时长
            AIUI_SLEEP(40);
        }

        // 音频写完后，要发CMD_STOP_WRITE停止写入消息
        SEND_AIUIMESSAGE4(AIUIConstant::CMD_STOP_WRITE, 0, 0, "data_type=audio");

        delete[] audio;
    } else {
        cout << "open file failed, path=" << TEST_AUDIO_PATH << endl;
    }

    cout << "write finish" << endl;
}

void startRecordAudio()
{
    SEND_AIUIMESSAGE4(
        AIUIConstant::CMD_START_RECORD, 0, 0, "data_type=audio,pers_param={\"uid\":\"\"},tag=record-tag");
}

void stopRecordAudio()
{
    SEND_AIUIMESSAGE1(AIUIConstant::CMD_STOP_RECORD);
}

void writeText(const string& text, bool needWakeup)
{
    AIUIBuffer textData = aiui_create_buffer_from_data(text.c_str(), text.length());
    if (needWakeup) {
        SEND_AIUIMESSAGE(AIUIConstant::CMD_WRITE, 0, 0, "data_type=text,pers_param={\"uid\":\"\"}", textData);
    } else {
        SEND_AIUIMESSAGE(AIUIConstant::CMD_WRITE, 0, 0, "data_type=text,need_wakeup=false", textData);
    }
}

/**
 * 测试超拟人语音合成，返回pcm数据。
 *
 * @param text
 */
void startHTS(const string& text, const string& tag)
{
    AIUIBuffer textData = aiui_create_buffer_from_data(text.c_str(), text.length());

    // 超拟人合成需要先给发音人开通授权，再设置scene=IFLYTEK.hts
    // 口语化等级oral_level: high, mid（默认值）, low
    // 情感强度emotion_scale: [-20, 20]，默认值: 0
    // 情感类型emotion: 0（中立，默认）, 1（调皮）, 2（安慰）, 3（可爱）, 4（鼓励）, 5（高兴）, 6（抱歉）, 7（撒娇）, 8（宠溺）, 9（严肃）
    //      10（困惑）, 11（害怕）, 12（悲伤）, 13（生气）
    string params = "voice_name=x4_lingxiaoxuan_oral,scene=IFLYTEK.hts,oral_level=mid,emotion_scale=0,emotion=0";
    if (!tag.empty()) {
        params.append(",tag=").append(tag);
    }

    SEND_AIUIMESSAGE(AIUIConstant::CMD_TTS, AIUIConstant::START, 0, params.c_str(), textData);
}


/**
 * 测试语音合成，返回pcm数据。
 *
 * @param text
 */
void startTTS(const string& text, const string& tag)
{
    AIUIBuffer textData = aiui_create_buffer_from_data(text.c_str(), text.length());
    string params = "voice_name=x5_lingxiaoyue_flow";
    if (!tag.empty()) {
        params.append(",tag=").append(tag);
    }

    // 使用发音人x4_lingxiaoying_em_v2合成，也可以使用其他发音人
    SEND_AIUIMESSAGE(AIUIConstant::CMD_TTS, AIUIConstant::START, 0, params.c_str(), textData);
}



static void GenerateMACAddress(char* mac)
{
    // reference: https://stackoverflow.com/questions/1779715/how-to-get-mac-address-of-your-machine-using-a-c-program/35242525
    int sock = socket(AF_INET, SOCK_DGRAM, 0);
    if (sock < 0) {
        return;
    };

    struct ifconf ifc{};
    char buf[1024];
    int success = 0;

    ifc.ifc_len = sizeof(buf);
    ifc.ifc_buf = buf;
    if (ioctl(sock, SIOCGIFCONF, &ifc) == -1) {
        return;
    }

    struct ifreq* it = ifc.ifc_req;
    const struct ifreq* const end = it + (ifc.ifc_len / sizeof(struct ifreq));
    struct ifreq ifr{};

    for (; it != end; ++it) {
        strcpy(ifr.ifr_name, it->ifr_name);
        if (ioctl(sock, SIOCGIFFLAGS, &ifr) == 0) {
            if (!(ifr.ifr_flags & IFF_LOOPBACK)) {    // don't count loopback
                if (ioctl(sock, SIOCGIFHWADDR, &ifr) == 0) {
                    success = 1;
                    break;
                }
            }
        } else {
            return;
        }
    }

    unsigned char mac_address[6];
    if (success) memcpy(mac_address, ifr.ifr_hwaddr.sa_data, 6);

    sprintf(mac,
            "%02x:%02x:%02x:%02x:%02x:%02x",
            mac_address[0],
            mac_address[1],
            mac_address[2],
            mac_address[3],
            mac_address[4],
            mac_address[5]);
    close(sock);
}

static void initSetting(bool log)
{
    AIUISetting::setAIUIDir(TEST_ROOT_DIR);
    AIUISetting::setMscDir(MSC_DIR);
    AIUISetting::setNetLogLevel(log ? aiui_debug : aiui_none);
    char mac[64] = {0};
    GenerateMACAddress(mac);
    // 为每一个设备设置唯一对应的序列号SN（最好使用设备硬件信息(mac地址，设备序列号等）生成），以便正确统计装机量，
    // 避免刷机或者应用卸载重装导致装机量重复计数
    AIUISetting::setSystemInfo(AIUI_KEY_SERIAL_NUM, mac);
    // 6.6.xxxx.xxxx版本设置用户唯一标识uid（可选，AIUI后台服务需要，不设置则会使用上面的SN作为uid）
    // 5.6.xxxx.xxxx版本SDK不能也不需要设置uid
    // AIUISetting::setSystemInfo(AIUI_KEY_UID, "1234567890");
}

int main(int argc,char* argv[]){
    ros::init(argc, argv, "tts_node");
    if (argc > 1) {
        gPcmPlayerIndex = atoi(argv[1]);
    }
    g_pListener = new REIListener;
    initSetting();
    // 打印SDK版本
    std::cout << "Version: " << getVersion() << std::endl;
    cout<<"==================="<<endl;
    // if (nullptr == g_pListener)
    //     g_pListener = new REIListener;
    cout<<"==================="<<endl;
    createAgent();
    cout<<"==================="<<endl;
    wakeup();
    cout << "writeText" << endl;
    // writeText("导航到卫生间");
    // writeText("导航到石家庄馆");
    // writeText("介绍一下深圳市光明区");
    // writeText("给我推荐一道今天适合吃的菜");
    writeText("介绍一下你自己");
    cout<<"++++++++++++++++++++"<<endl;
    // startTTSUrl("我叫小飞飞，是你的好朋友");
    // startTTS("这几天心里颇不宁静。今晚在院子里坐着乘凉，忽然想起日日走过的荷塘，在这满月的夜里，总该另有一 番样子吧。月亮渐渐地升高了，墙外马路上孩子们的欢笑，已经听不见了;妻在屋里拍着闰儿，迷迷糊糊地哼着眠歌。我悄悄地披了大衫，带上门出去。", "tts-tag");
    ros::spin();
    stop();
    destroyAgent();
    return 0;
}